/*
 * Copyright (C) 2018 Logos Technology Ltd.
 */

package com.logos.tool.location;

import android.content.Context;
import android.os.Vibrator;
import android.widget.Toast;

import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.location.CoordinateConverter;
import com.amap.api.location.DPoint;
import com.logos.tool.log.LogUtil;
import com.logos.tool.map.CoordinateTransform;

import static android.content.Context.VIBRATOR_SERVICE;

/**
 * Description : MapLocationOperation.
 * 调用例子
 * mOperation = new MapLocationOperation(getContext());
 * mOperation.setMapLocationChangedListener(this);
 * Transform transform = new TransformGcj02ToWgs84();
 * mOperation.setTransform(transform);
 * mOperation.setAccuracyLevel(MapLocationOperation.AccuracyLevel.HEIGHT);
 * mOperation.start();
 *
 * @author Young Ken
 * @date 16-12-9
 */
public class MapLocationOperation implements IMapLocationOperation {

    private static final String TAG = "MapLocationOperation";
    private static final int LAST_SATELLITES = 7;
    private AMapLocationClient mLocationClient = null;
    private Context mContext = null;
    private IMapLocationChangedListener mMapLocationChangedListener = null;
    private boolean mIsFollow = true;
    private boolean mIsStop = false;
    private int mLocationTimes = 0;
    private long mStartLocationTime = 0;
    private int mMeetLimitTimes = 0;

    private AccuracyLevel mLevel = AccuracyLevel.HEIGHT;
    private CoordinateConverter mCoordinateConverter;
    private Transform mTransform;
    private AMapLocationClientOption mOption;

    /**
     * constructor.
     *
     * @param context Context
     */
    public MapLocationOperation(Context context) {

        if (context == null) {
            throw new NullPointerException("parameter is null");
        }
        mContext = context.getApplicationContext();
        mOption = new AMapLocationClientOption();
        setDefaultOption();
    }

    private void initLocation() {
        //初始化client
        mLocationClient = new AMapLocationClient(mContext);
        //设置定位参数
        mLocationClient.setLocationOption(mOption);
        // 设置定位监听
        mLocationClient.setLocationListener(mLocationListener);
    }

    public void setAccuracyLevel(AccuracyLevel level) {

        mLevel = level;
    }

    /**
     * get location default option
     *
     * @return {@linkplain AMapLocationClientOption}
     */
    private AMapLocationClientOption setDefaultOption() {
        mOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
        mOption.setGpsFirst(false);//可选，设置是否gps优先，只在高精度模式下有效。默认关闭
        mOption.setHttpTimeOut(30000);//可选，设置网络请求超时时间。默认为30秒。在仅设备模式下无效
        mOption.setInterval(200);//可选，设置定位间隔。默认为2秒
        mOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Transport);
        mOption.setNeedAddress(true);//可选，设置是否返回逆地理地址信息。默认是ture
        mOption.setOnceLocation(false);//可选，设置是否单次定位。默认是false
        return mOption;
    }

    /**
     * 设置间隔时间，必须在{@link MapLocationOperation#start}之前执行
     *
     * @param time long
     */
    public void setInterval(long time) {
        mOption.setInterval(time);
    }

    AMapLocationListener mLocationListener = new AMapLocationListener() {

        @Override
        public void onLocationChanged(AMapLocation location) {

            if (null == location || mStartLocationTime == 0) {

                return;
            }

            mLocationTimes++;
            long differTime = System.currentTimeMillis() - mStartLocationTime;
            float accuracy = location.getAccuracy() == 0.0 ? 1000 : location.getAccuracy();

            LogUtil.debug(TAG,
                    "\n times " + mLocationTimes + ""
                            + "\n differTime " + differTime + ""
                            + "\n accuracy" + location.getAccuracy() + ""
                            + "\n satellites" + location.getSatellites() + ""
                            + "\n exception" + location.getErrorInfo() + ""
                            + "\n location" + location.getLongitude() + ","
                            + location.getLatitude());

            if (CoordinateTransform.outOfChina(location.getLatitude(), location.getLongitude())) {
                return;
            }
            if (mLevel.equals(AccuracyLevel.LOW)) {
                locationChange(location);
            } else if (mLevel.equals(AccuracyLevel.HEIGHT)) {
                if (accuracy < 50 || location.getSatellites() >= 3) {
                    locationChange(location);
                    mLocationTimes = 0;
                    mStartLocationTime = System.currentTimeMillis();
                    return;
                }

                if (mLocationTimes > 20 || differTime > 1000 * 15) {
                    locationChange(location);
                }
            } else if (mLevel.equals(AccuracyLevel.FOLLOW)) {
                if (accuracy < 20 && location.getSatellites() > LAST_SATELLITES) {
                    locationChange(location);
                    mLocationTimes = 0;
                    mStartLocationTime = System.currentTimeMillis();
                    return;
                }

                if (mLocationTimes > 10 || differTime > 1000 * 15) {
                    mLocationTimes = 0;
                    mStartLocationTime = System.currentTimeMillis();
                    showErrorMessage(location.getSatellites());
                }
            } else if (mLevel.equals(AccuracyLevel.SUPER)) {

                if (accuracy < 35 && location.getSatellites() > LAST_SATELLITES) {
                    mMeetLimitTimes++;
                    if (mMeetLimitTimes > 3) {
                        locationChange(location);
                        mLocationTimes = 0;
                        mStartLocationTime = System.currentTimeMillis();
                        return;
                    }
                }

                if (mLocationTimes > 10 || differTime > 1000 * 15) {
                    mLocationTimes = 0;
                    mStartLocationTime = System.currentTimeMillis();
                    showErrorMessage(location.getSatellites());
                }
            }
        }
    };


    private void showErrorMessage(int satellitesNum) {

        if (satellitesNum <= 3) {
            Toast.makeText(mContext, "定位失败,GPS信号弱,请到室外", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(mContext, "定位失败,定位精度差,请等候", Toast.LENGTH_LONG).show();
        }
        Vibrator vibrator = (Vibrator) mContext.getSystemService(VIBRATOR_SERVICE);
        vibrator.vibrate(200);
    }

    private void locationChange(AMapLocation location) {

        mLocationTimes = 0;
        mStartLocationTime = System.currentTimeMillis();

        DPoint result = new DPoint(location.getLatitude(), location.getLongitude());

        if (mTransform != null) {
            result = mTransform.transform(result);
        }
        if (result != null) {
            location.setLongitude(result.getLongitude());
            location.setLatitude(result.getLatitude());
        }

        ILocation loc = new ABaseLocation(location);
        if (mMapLocationChangedListener != null && !mIsStop) {
            mMapLocationChangedListener.onMapLocationChangedListener(loc);
        }
    }

    @Override
    public void start() {

        mIsStop = false;
        mLocationTimes = 0;
        mStartLocationTime = System.currentTimeMillis();
        if (mLocationClient == null) {
            initLocation();
        }
        mLocationClient.startLocation();
//
//        if (!mLocationClient.isStarted()) {
//            mLocationClient.startLocation();
//        } else {
//            mLocationClient.startLocation();
//        }
    }

    @Override
    public void stop() {

        mIsStop = true;
        mLocationTimes = 0;
        mStartLocationTime = 0;
        mMeetLimitTimes = 0;
        if (mLocationClient != null && mLocationClient.isStarted()) {
            mLocationClient.stopLocation();
        }
    }

    @Override
    public void destroy() {

        if (null != mLocationClient) {
            stop();
            mLocationClient.onDestroy();
            mLocationClient = null;
        }
    }

    @Override
    public void follow(boolean bool) {

        mIsFollow = bool;
    }

    @Override
    public boolean isStart() {

        if (mLocationClient != null) {
            return mLocationClient.isStarted();
        }
        return false;
    }

    @Override
    public boolean isFollow() {

        return mIsFollow;
    }

    /**
     * 设置坐标转换算法
     *
     * @param transform Transform 接口
     */
    public void setTransform(Transform transform) {
        mTransform = transform;
    }

    @Override
    public void setMapLocationChangedListener(IMapLocationChangedListener
                                                      mapLocationChangedListener) {

        mMapLocationChangedListener = mapLocationChangedListener;
    }

    public enum AccuracyLevel {
        LOW,
        HEIGHT,
        FOLLOW,
        SUPER,
        USUALLY
    }
}
